查看原文
其他

7 个省时高效的 pytest 特性和插件

阿旭 Python开发者 2021-02-01

(给Python开发者加星标,提升Python技能

英文:Miguel Brito,翻译:Python开发者 / 阿旭

在本篇教程中,我们将会学习几个超实用的 pytest 特性和插件,可以加速开发过程,大大节省时间提高效率。它们非常简单,并且可以马上开始使用。

目录

  1. 如何在第一次遇到测试用例运行失败的情况时,就停止测试流程
  2. 如何在重新运行测试流程时,只运行上一次运行失败的测试用例
  3. 如何从上一次运行失败的测试用例开始,重新运行整个测试流程
  4. 如何将运行失败的测试用例的局部变量信息显示出来
  5. 如何在运行整个测试流程时,只运行其中的一部分
  6. 如何以并行的方式来运行我们的测试流程
  7. 如何重新运行“结果不稳定”的测试用例,以及如何消除这种“间歇性故障”
  8. 小结

1.如何在第一次遇到测试用例运行失败的情况时,就停止测试流程

完整运行一个大型项目的测试套件可能需要很长时间。而且无论是在本地运行还是在 CI 服务器上运行,在耐心等待之后得到一个运行失败的结果总是令人沮丧的。在某些情况下,你可能会想要在第一次遇到运行失败的情况时就中止测试流程,以便于立即修复这个出现问题的测试用例。幸运的是,pytest 为此提供了一个非常方便的命令行选项:-x 或 --exitfirst

$ pytest -x tests/

2.如何在重新运行测试流程时,只运行上一次运行失败的测试用例

在本地进行开发时,你可能会倾向于在运行完所有的测试用例之后再将代码推送至仓库。如果你正在做的项目是一个只有几个测试用例的小项目,这样做完全没有问题;但是如果该项目的测试用例完整地运行一次需要好几分钟,那么你可能会希望在每次运行时仅运行上一次运行失败的那些测试用例。pytest 允许通过--lf 或 --last-failed 命令行选项来执行这样的操作。通过这种方式,你可以节省宝贵的时间并对该项目进行更快的迭代!

$ pytest --lf tests/

3.如何从上一次运行失败的测试用例开始,重新运行整个测试流程

与前文中的命令类似,重新运行全部的测试用例可能会对我们的测试很有帮助。这里要讲的不同点是:当你想要从上一次运行失败的测试用例开始重新运行整个测试流程时,可以通过在命令行中使用 --ff 或 --failed-first 选项来达成目的。

$ pytest --ff tests/

4.如何将运行失败的测试用例的局部变量信息显示出来

至此,我们已经了解了快速迭代的重要性以及它如何为你节省宝贵的时间。同样地,获取关键的提示信息来帮助我们调试失败的测试用例也是非常重要的。通过在命令行中使用 --showlocals 或 -l 选项,我们就可以在 traceback 中看到局部变量的信息。

$ pytest tests/test_variables.py -l               
================ test session starts ================
...                                                                                                                                                
tests/test_variables.py FF                                                                                                                                                     [100%]

================ FAILURES ================
_____________________________________________________________________________ test_local_variables[name] _____________________________________________________________________________

key = 'name'

    @pytest.mark.parametrize("key", ["name""age"])
    def test_local_variables(key):
        result = person_info()
>       assert key in result
E       AssertionError: assert 'name' in {'height': 180}

key        = 'name'
result     = {'height': 180}

tests/test_variables.py:11: AssertionError
_____________________________________________________________________________ test_local_variables[age] ______________________________________________________________________________

key = 'age'

    @pytest.mark.parametrize("key", ["name""age"])
    def test_local_variables(key):
        result = person_info()
>       assert key in result
E       AssertionError: assert 'age' in {'height': 180}

key        = 'age'
result     = {'height': 180}

tests/test_variables.py:11: AssertionError
================ short test summary info ================
FAILED tests/test_variables.py::test_local_variables[name] - AssertionError: assert 'name' in {'height': 180}
FAILED tests/test_variables.py::test_local_variables[age] - AssertionError: assert 'age' in {'height': 180}
================ 2 failed in 0.05s ================

5.如何在运行整个测试流程时,只运行其中的一部分

有时候你可能需要只运行完整测试流程的一部分。一种方式是通过仅运行单个文件所对应的测试用例来实现。比如,你可以执行 pytest test_functions.py。虽然这种方式比运行全部测试用例要好些,但是我们还可以优化它。通过使用命令行选项 -k,我们可以指定一些关键字表达式,然后 pytest 将会根据这些关键字表达式来选择运行哪些测试用例。

# tests/test_variables.py
def test_asdict():
    ...

def test_astuple():
    ...

def test_aslist():
    ...

这里假设你需要运行前两个测试用例,那么你可以传递一个由 or 作为分隔符的关键字列表:

$ pytest -k "asdict or astuple" tests/test_variables.py

输出:

$ pytest -k "asdict or astuple" tests/test_variables.py
==================================== test session starts ====================================
...                                          

tests/test_variables.py ..                                                            [100%]

============================== 2 passed, 1 deselected in 0.02s ==============================

6.如何以并行的方式来运行我们的测试流程

一个项目所拥有的测试用例越多,运行它们所花费的时间也会越长。这听起来像是一个毋庸置疑的说法,但它通常会被忽视。一个接一个地运行测试用例是对时间的极大浪费,而加速运行的最好方法就是并行运行,并且利用好多个 CPU。

但遗憾的是,pytest 暂时没有类似的功能,所以我们得依赖插件。对于这个功能点,最好的 pytest 插件就是 pytest-xdist。

为了将测试用例发送到多个 CPU 运行,我们可以直接使用 -n 或--numprocesses 选项。

$ pytest -n NUMCPUS

如果你不知道有多少个可用的 CPU,你可以使用 auto 参数来告知 pytets-xdist 直接在所有可用的 CPU 上执行测试。

$ pytest -n auto

7.如何重新运行“结果不稳定”的测试用例,以及如何消除这种“间歇性故障”

最令人沮丧的情况之一就是所有测试用例在本地都能成功运行,但在 CI 服务器上却运行失败。造成此类失败的原因可能有很多种,但大多数情况下都是由于“不稳定”造成的。“不稳定”的测试是指:测试在运行时会以不确定的方式出现“间接性故障”。通常情况下,将它们重新运行一次就好了。但问题是,如果一个测试流程耗时很长,你需要重新触发 CI 步骤然后再等待好几分钟。这将会产生巨大的时间消耗,不过幸运的是,我们可以避免这种情形。

为了改善这一点,最理想的状态是我们可以重新运行这些“不稳定”的测试。这样一来,就可以增加其运行通过的机会,避免 CI 中的步骤完全构建失败。

实现上述目标最好用的 pytest 插件是 pytest-rerunfailures。它可以根据我们的需求来重新运行测试,从而消除“间歇性故障”。

使用该插件的最简方式是通过 --reruns 选项来设置你想让测试流程重新运行的最大次数:

$ pytest --reruns 5

如果事先就知道哪个单独的测试用例会有问题,你也可以用下面的方式来将它标记为“需要重新运行”:

@pytest.mark.flaky(reruns=5)
def test_flaky():
    assert get_resut() is True

小结

大型测试套件可以为项目质量带来保证,但同时也会增加成本。长时间运行测试流程将会占用大量的开发时间并放缓迭代速度。通过巧妙地使用 pytest 特性和插件生态,可以显著加快我们的开发进程。在本教程中,我们研究了 7 个技巧,通过使用它们,可以改善开发体验,同时减少测试流程运行时产生的时间浪费。


- EOF -

推荐阅读  点击标题可跳转

1、下个十年,Python 的“王者”地位还能保住吗?

2、Python 简史了解下?

3、Python 国产库推荐之 musicpy


觉得本文对你有帮助?请分享给更多人

推荐关注「Python开发者」,提升Python技能

点赞和在看就是最大的支持❤️

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存